package log

import (
	"context"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"time"
)

const (
	LevelInfo  = "info"
	LevelWarn  = "warn"
	LevelError = "error"
	LevelFatal = "fatal"
	LevelDebug = "debug"
	LevelTrace = "trace"

	RequestIDFieldKey       = "request_id"
	UserIDFieldKey          = "user_id"
	VersionFieldKey         = "version"
	ApplicationNameFieldKey = "application"
	BuildFieldKey           = "build"
)

var (
	version         string
	build           string
	applicationName string
)

type (
	requestIDKey struct{}
	userIDKey    struct{}
)

func SetFormat(fmt string) {
	if fmt != "json" && fmt != "text" {
		logrus.WithFields(
			logrus.Fields{
				"format": fmt,
			}).Warn("Unknown logging format specified, using 'text', either 'json' or 'text'.")
	}

	if fmt == "json" {
		logrus.SetFormatter(&logrus.JSONFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	} else {
		logrus.SetFormatter(&logrus.TextFormatter{
			FullTimestamp: true,
		})
	}
}

func SetLevel(lvl string) {
	if lvl == "" {
		lvl = LevelInfo
	}

	level, err := logrus.ParseLevel(lvl)

	if err != nil {
		logrus.WithFields(logrus.Fields{
			"level": lvl,
		}).Warn("Unexpected log level, setting to 'info'.")
		logrus.SetLevel(level)
	} else {
		logrus.SetLevel(level)
	}

	// Sync level setting with Gin
	if lvl == LevelDebug {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}
}

func SetVersion(ver string) {
	version = ver
}

func SetBuild(b string) {
	build = b
}

func SetApplicationName(name string) {
	applicationName = name
}

func NewRequestIDContext(ctx context.Context, requestID string) context.Context {
	return context.WithValue(ctx, requestIDKey{}, requestID)
}

func FromRequestIDContext(ctx context.Context) string {
	val := ctx.Value(requestIDKey{})
	if val != nil {
		if str, ok := val.(string); ok {
			return str
		}
	}
	return ""
}

func NewUserIDContext(ctx context.Context, userID int) context.Context {
	return context.WithValue(ctx, userIDKey{}, userID)
}

func FromUserIDContext(ctx context.Context) int {
	val := ctx.Value(userIDKey{})
	if val != nil {
		if num, ok := val.(int); ok {
			return num
		}
	}
	return -1
}

func WithContext(ctx context.Context) *logrus.Entry {
	if ctx == nil {
		ctx = context.Background()
	}

	fields := map[string]interface{}{
		ApplicationNameFieldKey: applicationName,
		VersionFieldKey:         version,
		BuildFieldKey:           build,
	}

	if val := FromRequestIDContext(ctx); val != "" {
		fields[RequestIDFieldKey] = val
	}

	if val := FromUserIDContext(ctx); val != -1 {
		fields[UserIDFieldKey] = val
	}

	return logrus.WithContext(ctx).WithFields(fields)
}
